package cate.game.pvp.eliterace.handler;

import cate.common.table.d.GDEliteRace;
import cate.game.GameBody;
import cate.game.event.eliterace.EliteRaceTimerEvent;
import cate.game.framework.advanced.AdvancedLongAttributes;
import cate.game.play.part.PlaySide;
import cate.game.pvp.eliterace.data.*;
import cate.game.pvp.eliterace.handler.EliteRaceRankRwdOperation.QueryParam;
import cate.game.role.Role;
import cate.game.role.RoleSnapshot;
import cate.game.role.fight.FuncPosition;
import cate.game.role.fight.Position;
import cate.game.role.pvp.eliterace.msg.*;
import cate.game.role.pvp.eliterace.vo.EliteRaceBetVO;
import easy.java.practice.errorcode.EcResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static cate.common.table.d.GDFunc.ADVENTURE;
import static cate.common.table.d.GDFunc.ELITE_RACE;
import static cate.game.client.msg.MsgSender.send;
import static cate.game.event.eliterace.EliteRaceTimerEvent.EventType.*;
import static cate.game.event.eliterace.EliteRaceTimerEvent.EventType.REST;
import static cate.game.pvp.eliterace.util.EliteRaceSpecUtils.*;

@Slf4j
public class LocalEliteRaceHandler extends EliteRaceHandler {
    private final EliteRacePsOperation psOperation;
    private final EliteRaceRankHandler rankHandler;
    private final EliteRaceMessageHandler messageHandler;

    public LocalEliteRaceHandler(GameBody gameBody) {
        super(gameBody);
        this.psOperation = Objects.requireNonNull(getDataManager().getPsOperation());
        this.rankHandler = Objects.requireNonNull(getDataManager().getRankHandler());
        this.messageHandler = new BothEliteRaceMessageHandler(
                getDataManager()::getRecordOperation,
                getDataManager()::getPsOperation,
                getDataManager()::getBetOperation,
                getDataManager()::getRedPacketOperation,
                getDataManager()::getRankRwdOperation
        );
    }

    @Override
    public EcResult<Void> admire(String uid, int rank) {
        return this.rankHandler.admire(uid, rank);
    }

    @Override
    public int getIndex(String uid) {
        return this.rankHandler.getIndex(uid);
    }

    @Nullable
    @Override
    public EliteRaceRank getData(String uid) {
        return this.rankHandler.getData(uid);
    }

    @Override
    public Flux<EliteRaceRank> getRankFlow() {
        return this.rankHandler.getRankFlow();
    }


    @Override
    public void updatePosition(Role role, FuncPosition funcPosition) {
        EcResult<PlaySide> r = getGameBody().fight.side.createPlaySideByRole(role, funcPosition.funcId, 1);
        if (r.ok() && Objects.nonNull(r.data)) {
            PlaySide ps = r.data;
            ps.snapshot.activeTime = 0;
            EcResult<EliteRacePs> psr = EliteRacePs.compressEc(ps, role.getPvp().eliteRace.points);
            if (psr.ok() && Objects.nonNull(psr.data)) {
                this.psOperation.update(psr.data);
            }
        }
    }

    @Override
    public boolean hasRecvdRedPacket(String roleUid, EliteRaceSpec spec) {
        return messageHandler.hasRecvdRedPacket(roleUid, spec);
    }

    public void handleSyncLocalPosition(Role role) {
        Position position = role.getFight().getPosition(ADVENTURE);
        if (Objects.isNull(position)) {
            log.error("发生错误，找不到玩家的主线冒险阵容");
            return;
        }

        FuncPosition funcPosition = new FuncPosition();
        funcPosition.funcId = ELITE_RACE;
        funcPosition.positions.add(position.copy());
        funcPosition.init();

        EcResult r = role.getGame().fight.position.updatePosition(role, funcPosition);
        if (!r.ok()) {
            log.error("第一次设置玩家王者赛阵容出现错误: {}", r.message);
        }
    }

    @Override
    public EcResult<EliteRaceBetRoundResp> handleBetRoundReq(EliteRaceBetRoundReq req) {
        return this.messageHandler.handleBetRoundReq(req);
    }

    @Override
    public EcResult<EliteRaceBetRoundReportResp> handleBetRoundReportReq(EliteRaceBetRoundReportReq req) {
        return this.messageHandler.handleBetRoundReportReq(req);
    }

    @Override
    public EcResult<EliteRaceRoundResp> handleRoundReq(String uid, EliteRaceRoundReq req) {
        return this.messageHandler.handleRoundReq(uid, req);
    }

    @Override
    public EcResult<EliteRaceRoundReportResp> handleRoundReportReq(String uid, EliteRaceRoundReportReq req) {
        return this.messageHandler.handleRoundReportReq(uid, req);
    }

    @Override
    public EcResult<EliteRaceGroupRoundResp> handleGroupRoundReq(EliteRaceGroupRoundReq req) {
        return this.messageHandler.handleGroupRoundReq(req);
    }

    @Override
    public EcResult<EliteRaceGroupRoundReportResp> handleGroupRoundReportReq(EliteRaceGroupRoundReportReq req) {
        return this.messageHandler.handleGroupRoundReportReq(req);
    }

    @Override
    public EcResult<EliteRaceBetRecord> handleBetReq(EliteRaceSpec spec, EliteRaceBetReq req) {
        return this.messageHandler.handleBetReq(spec, req);
    }

    @Override
    public EcResult<EliteRaceBetRecord> handleGetBetRecord(EliteRaceSpec spec) {
        return this.messageHandler.handleGetBetRecord(spec);
    }

    @Override
    public EcResult<Void> handleUpdatePs(EliteRaceSpec spec, EliteRacePs eps) {
        return this.messageHandler.handleUpdatePs(spec, eps);
    }

    @Override
    public EcResult<Void> handleBetReq(Role role, EliteRaceBetReq req) {
        EcResult<Void> r = super.handleBetReq(role, req);

        if (!r.ok()) {
            return r;
        }

        EliteRaceKV kv = getDataManager().getData();

        EliteRaceSpec spec = EliteRaceSpec.of(
                kv.getSpec().getSeason(),
                kv.getSpec().getPeriod(),
                kv.getSpec().getGroup(),
                kv.getSpec().getRound()
        );

        EcResult<EliteRaceBetRecord> br = handleBetReq(kv.getSpec(), req);

        if (!br.ok()) {
            return r.fail("竞猜失败");
        }

        if (Objects.isNull(br.data)) {
            return r.fail("竞猜失败");
        }

        EliteRaceBetVO betVO = EliteRaceBetVO.of(br.data, GDEliteRace.FIXED_BET_NUM, req.side);

        role.getPvp().eliteRace.betPO.handleBet(req, betVO);

        getGameBody().role.activeForOnline(ar -> ar.getPvp().eliteRace.betPO.updateNotice(spec, br.data));

        return r.success();
    }

    @Override
    public EcResult<List<EliteRaceRedPacketRecord>> handleGetRedPacket(RoleSnapshot snapshot, EliteRaceSpec spec, GameBody game) {
        return this.messageHandler.handleGetRedPacket(snapshot, spec, game);
    }

    @Override
    public EcResult<List<EliteRaceRedPacketRecord>> handleQueryRedPacket(EliteRaceSpec spec, String uid) {
        return this.messageHandler.handleQueryRedPacket(spec, uid);
    }

    @Override
    public EcResult<List<EliteRaceRankRwd>> handleQueryRankRwd(QueryParam param) {
        return this.messageHandler.handleQueryRankRwd(param);
    }

    @Override
    public EcResult<EliteRaceGetPsResp> handleGetPsReq(EliteRaceGetPsReq req) {
        return this.messageHandler.handleGetPsReq(req);
    }

    @Override
    public EcResult<List<EliteRaceOpponent>> handleQueryPs(int srvId) {
        return this.messageHandler.handleQueryPs(srvId);
    }

    @Override
    public EcResult<Map<String, String>> handleQueryPlayers(int srvId, EliteRaceSpec spec) {
        return this.messageHandler.handleQueryPlayers(srvId, spec);
    }

    @Override
    public EcResult<List<EliteRaceBetRecord>> handleQueryAll() {
        return this.messageHandler.handleQueryAll();
    }

    @Override
    public void onOpenEvent(EliteRaceTimerEvent.OpenEvent openEvent) {
        getGameBody().role.activeForOnline(
                role -> send(role::sendNow, new EliteRaceDataResp(
                        getDataManager().getData().getSpec(),
                        role.getPvp().eliteRace)));
    }

    @Override
    public void onCloseEvent(EliteRaceTimerEvent.ClosedEvent closedEvent) {
        getDataManager().clear();
    }

    @Override
    public void onRankSettleEvent(EliteRaceTimerEvent.RankSettleEvent rankSettleEvent) {
        EliteRaceKV kv = getDataManager().getData();
        AdvancedLongAttributes attributes = kv.getEventAttributes();
        if (attributes.hasAttribute(RANK_SETTLE.getFlag())) {
            return;
        }
        kv.setSpec(rankSettleEvent.getSpec());

        EcResult<List<EliteRaceOpponent>> r = handleQueryPs(getGameBody().config.srvId);
        if (!r.ok() || CollectionUtils.isEmpty(r.data)) {
            attributes.setAttribute(RANK_SETTLE.getFlag());
            return;
        }
        List<EliteRaceOpponent> queryList = r.data;
        queryList.stream()
                .map(opp -> opp.getSnapshot().base.uid)
                .forEach(this::handleSyncPosition);

        attributes.setAttribute(RANK_SETTLE.getFlag());
    }

    @Override
    public void onInvitationEvent(EliteRaceTimerEvent.InvitationEvent invitationEvent) {
        EliteRaceKV kv = getDataManager().getData();
        AdvancedLongAttributes attributes = kv.getEventAttributes();
        if (attributes.hasAttribute(INVITATION.getFlag())) {
            return;
        }
        kv.setSpec(invitationEvent.getSpec());
        queryInvitedOpponentsAndHandle();
        attributes.setAttribute(INVITATION.getFlag());
    }

    @Override
    public void onTrialEvent(EliteRaceTimerEvent.TrialEvent trialEvent) {
        EliteRaceKV kv = getDataManager().getData();
        AdvancedLongAttributes attributes = kv.getEventAttributes();
        if (attributes.hasAttribute(getTrialFlag(trialEvent))) {
            return;
        }
        final EliteRaceSpec nowSpec = trialEvent.getSpec();
        kv.setSpec(nowSpec);
        updatePositionAndHandle(nowSpec);
        getGameBody().role.activeForOnline(role -> {
            queryBetRecordAndHandle(role, nowSpec);
            noticeUpdateSpec(role, nowSpec);
        });
        attributes.setAttribute(getTrialFlag(trialEvent.getSpec()));
    }

    @Override
    public void onKoEvent(EliteRaceTimerEvent.KoEvent koEvent) {
        EliteRaceKV kv = getDataManager().getData();
        AdvancedLongAttributes attributes = kv.getEventAttributes();
        if (attributes.hasAttribute(getKoFlag(koEvent))) {
            return;
        }
        final EliteRaceSpec nowSpec = koEvent.getSpec();
        kv.setSpec(nowSpec);
        updatePositionAndHandle(nowSpec);
        getGameBody().role.activeForOnline(role -> {
            queryBetRecordAndHandle(role, nowSpec);
            noticeUpdateSpec(role, nowSpec);
        });
        attributes.setAttribute(getKoFlag(koEvent));
    }

    @Override
    public void onFinalEvent(EliteRaceTimerEvent.FinalEvent finalEvent) {
        EliteRaceKV kv = getDataManager().getData();
        AdvancedLongAttributes attributes = kv.getEventAttributes();
        if (attributes.hasAttribute(getFinalFlag(finalEvent))) {
            return;
        }
        final EliteRaceSpec nowSpec = finalEvent.getSpec();
        kv.setSpec(nowSpec);
        updatePositionAndHandle(nowSpec);
        getGameBody().role.activeForOnline(role -> {
            queryBetRecordAndHandle(role, nowSpec);
            noticeUpdateSpec(role, nowSpec);
        });
        attributes.setAttribute(getFinalFlag(finalEvent));
    }

    private void queryBetRecordAndHandle(Role role, EliteRaceSpec nowSpec) {
        if (isBetRwd(nowSpec)) {
            EliteRaceSpec querySpec = EliteRaceSpec.of(
                    nowSpec.getSeason(),
                    nowSpec.getPeriod(),
                    nowSpec.getGroup(),
                    nowSpec.getRound()
            );
            EcResult<EliteRaceBetRecord> r = handleGetBetRecord(querySpec);
            if (r.ok()) {
                role.getPvp().eliteRace.betPO.handleBetRwd(r.data);
            }
        }
    }

    @Override
    public void onRankSwitchEvent(EliteRaceTimerEvent.RankSwitchEvent rankSwitchEvent) {
        EliteRaceKV kv = getDataManager().getData();
        AdvancedLongAttributes attributes = kv.getEventAttributes();
        if (attributes.hasAttribute(RANK_SWITCH.getFlag())) {
            return;
        }
        kv.setSpec(rankSwitchEvent.getSpec());
        log.info("开始发送 {} 服 [精英赛] 奖励", getGameBody().config.srvId);
        queryRankRwdAndHandle();
        getGameBody().role.activeForOnline(role -> {
            role.getPvp().eliteRace.betPO.onSeasonSwitch();
            noticeUpdateSpec(role, rankSwitchEvent.getSpec());
        });
        attributes.setAttribute(RANK_SWITCH.getFlag());
    }

    @Override
    public void onShowEvent(EliteRaceTimerEvent.ShowEvent showEvent) {
        EliteRaceKV kv = getDataManager().getData();
        AdvancedLongAttributes attributes = kv.getAttributes();
        if (attributes.hasAttribute(SHOW.getFlag())) {
            return;
        }
        kv.setSpec(showEvent.getSpec());
        getGameBody().role.activeForOnline(role -> noticeUpdateSpec(role, showEvent.getSpec()));
        attributes.setAttribute(SHOW.getFlag());
    }

    @Override
    public void onRestEvent(EliteRaceTimerEvent.RestEvent restEvent) {
        EliteRaceKV kv = getDataManager().getData();
        AdvancedLongAttributes attributes = kv.getAttributes();
        if (attributes.hasAttribute(REST.getFlag())) {
            return;
        }
        kv.setSpec(restEvent.getSpec());
        getGameBody().role.activeForOnline(role -> noticeUpdateSpec(role, restEvent.getSpec()));
        attributes.setAttribute(REST.getFlag());
    }

}
